package com.firsteapps.mobile.android.puzzle.game;

import java.util.Random;
import java.util.Vector;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.Log;

public class Puzzle {

	Vector<Tile> mTiles = new Vector<Tile>();
	private int mWidth;
	private int mHeight;
	private boolean mMoving = false;
	private Bitmap image;
	private Rect rectangle;
	private int size;

	private boolean created = false;
	private PuzzleInfo info;

	public Puzzle(Bitmap bmp, PuzzleInfo info) {
		image = bmp;
		this.info = info;
	}

	public void create(int size, int totalWidth, int totalHeight) {
		Log.d("DrawView", "Puzzle.Create");
		this.size = size;
		int tileBmpWidth = image.getWidth() / size;
		int tileBmpHeight = image.getHeight() / size;

		int tileWidth = tileBmpWidth * totalWidth / image.getWidth();//totalWidth / size;
		int tileHeight = tileBmpHeight * tileWidth / tileBmpWidth;//totalHeight / size;

		if (tileHeight * size > totalHeight) {
			tileHeight = tileBmpHeight * totalHeight / image.getHeight();
			tileWidth = tileBmpWidth * tileHeight / tileBmpHeight;
		}

		this.mWidth = tileWidth * size;//totalWidth;
		this.mHeight = tileHeight * size;//totalHeight;
		rectangle = new Rect(0, 0, this.mWidth, this.mHeight);
		rectangle.offset((totalWidth - this.mWidth) / 2, (totalHeight - this.mHeight) / 2);

		int prog = 0;
		Point[] tilePositions = info.getTilePositions();
		while (true) {
			try {
				mTiles.removeAllElements();
				for (int y = 0; y < size; y++)
					for (int x = 0; x < size; x++) {
						Bitmap tileImage = Bitmap.createBitmap(image, x * tileBmpWidth, y * tileBmpHeight, tileBmpWidth,
								tileBmpHeight);
						tileImage = Bitmap.createScaledBitmap(tileImage, tileWidth, tileHeight, true);
						Point targetPt = new Point(x, y);
						Point pt = tilePositions == null ? targetPt : tilePositions[prog++];
						Tile t = new Tile(pt, targetPt, this, tileImage);
						mTiles.add(t);

					}
				break;
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println(e.getMessage());
				if (tilePositions != null)
					tilePositions = null;
				else
					break;
			}
		}

		if (tilePositions == null) {
			shuffle();
			System.out.println("Shuffle called");
		}
		created = true;
	}

	public Point[] getTilePositions() {
		Point[] ppts = new Point[mTiles.size()];
		int idx = 0;
		for (Tile t : mTiles) {
			ppts[idx++] = t.getOffset();
		}
		return ppts;
	}

	public PuzzleInfo getInfo() {
		return info;
	}

	public void setInfo(PuzzleInfo info) {
		this.info = info;
	}

	private void replaceTiles(Tile first, Tile second) {
		int firstX = first.getCurrentX();
		int firstY = first.getCurrentY();
		int secondX = second.getCurrentX();
		int secondY = second.getCurrentY();
		first.move(secondX, secondY);
		second.move(firstX, firstY);
	}

	public int[] getValues() {
		int[] values = new int[mTiles.size()];
		for (int i = 0; i < values.length; i++) {
			Tile tile = mTiles.get(i);
			int position = tile.getOffset().y * getSize() + tile.getOffset().x;
			values[position] = i;
		}

		return values;
	}

	private boolean isSolvable() {
		int parity = 0;
		int[] currentPositions = getValues();
		for (int tile = 1; tile < mTiles.size(); tile++) {
			for (int tileBefore = 0; tileBefore < tile; tileBefore++) {
				if (currentPositions[tileBefore] > currentPositions[tile])
					parity++;
			}
		}
		parity -= mTiles.size() - 1 - getHiddenTilePossition();
		if (getSize() % 2 == 0 && (getHiddenTilePossition() / getSize()) % 2 == 0) {
			return parity % 2 == 1;
		} else
			return parity % 2 == 0;
	}

	public void shuffle() {
		Random rnd = new Random();
		rnd.setSeed(System.currentTimeMillis());
		int moves = 100 + rnd.nextInt(100);
		while (--moves >= 0) {
			int firstTile = rnd.nextInt(mTiles.size());
			int secondTile = rnd.nextInt(mTiles.size());
			if (firstTile == secondTile)
				secondTile = (firstTile + 1) % mTiles.size();

			replaceTiles(mTiles.get(firstTile), mTiles.get(secondTile));
		}

		while (!isSolvable()) {
			int firstTile = rnd.nextInt(mTiles.size());
			int secondTile = rnd.nextInt(mTiles.size());
			if (firstTile == secondTile)
				secondTile = (firstTile + 1) % mTiles.size();

			replaceTiles(mTiles.get(firstTile), mTiles.get(secondTile));
		}

		info.setMoveCount(0);
	}

	public void Draw(Canvas canvas) {
		for (int i = 0; i < mTiles.size(); ++i) {
			if (mTiles.get(i).isHiddenTile())
				continue;

			mTiles.get(i).Draw(canvas);
		}
//		canvas.drawRect(getRectangle(), Paints.getBorderPaint());
	}

	public Tile getTile(int pointX, int pointY) {
		for (Tile t : mTiles) {
			if (t.contains(pointX, pointY))
				return t;
		}

		return null;
	}

	public int getHiddenTilePossition() {
		for (int i = 0; i < mTiles.size(); ++i) {
			if (mTiles.get(i).isHiddenTile()) {
				int x = mTiles.get(i).getOffset().x;
				int y = mTiles.get(i).getOffset().y;
				return y * getSize() + x;
			}
		}

		return -1;
	}

	public boolean isAdjacentTile(Tile t) {
		if (t.isHiddenTile())
			return false;

		int x = t.getOffset().x;
		int y = t.getOffset().y;
		int position = y * getSize() + x;
		return isAdjacent(getHiddenTilePossition(), position, getSize());
	}

	private boolean isAdjacent(int mainIndex, int checkIndex, int size) {
		int left = mainIndex % size == 0 ? -1 : mainIndex - 1;
		int right = (mainIndex + 1) % size == 0 ? -1 : mainIndex + 1;
		int up = mainIndex < size ? -1 : mainIndex - size;
		int down = mainIndex < getSize() * (getSize() - 1) ? mainIndex + getSize() : -1;
		return checkIndex == left || checkIndex == right || checkIndex == up || checkIndex == down;
	}

	public int moveTile(Tile movingTile) {
		replaceTiles(movingTile, mTiles.get(mTiles.size() - 1));
		setMoving(true);
		// info.setTilePositions(getTilePositions());

		if (created)
			if (info.increaseMoveCount())
				return 1;
			else
				return 2;
		return 0;
	}

	int getWidth() {
		return mWidth;
	}

	int getHeight() {
		return mHeight;
	}

	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	public void resetMoving() {
		mMoving = false;

	}

	public boolean isMoving() {
		return mMoving;
	}

	public void setMoving(boolean mMoving) {
		this.mMoving = mMoving;
	}

	public Rect getRectangle() {
		return rectangle;
	}

	public boolean isCreated() {
		return created;
	}

	public void setCreated(boolean created) {
		this.created = created;
	}

	public boolean isCompleted() {
		for (Tile t : mTiles) {
			if (!t.isToTargetPosition())
				return false;
		}
		return true;
	}

	public int getMoveCount() {

		return info.getMoveCount();
	}

}